import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;



public class RobotThread extends Thread{
	private Robot robot;
	private ArrayList<Point> moveList;
	private ArrayList<Path> pathList;
	private Path path, selectedPath;
	private Robot.State robotState;
	public GamePiece collectionTarget;
	private final int movesKnown = 5; 
	private int currentMove, arrayIdentifier;
	private static Path[] selectedPaths;
	private Point currentPoint;
	private Comparable[] pathListasArray; 
	volatile private boolean running = true;


	public RobotThread(Robot robot){

		//gets the robot and state
		this.robot = robot;
		robotState = robot.getState();

		//initializes lists and move count
		moveList = new ArrayList<Point>();
		pathList = new ArrayList<Path>();
		currentMove = 0;
	}

	public void run(){

		while(true){
			//infinite loop happens but code only runs if goAgain is called which happens every 100 milliseconds
			while(running){
				try{
					//Attempts to make a move from the first of the ten closest path. If the path is null or out of moves the catch blocks run the
					//recursive algorithm
					selectedPath = pathList.get(0);
					int i = 0;
					while(true){
						if(robot.moveIsValid(selectedPath.getPointAt(currentMove))){
							break;
						}
						else{
							selectedPath = pathList.get(i);
							i++;
						}
					}				

					RoboticsProjectPanel.move(robot, selectedPath.getPointAt(currentMove));
					currentMove++;

					if(robot.getState() == Robot.State.collect){
						//Attempts to collect
						checkCollecting();
					}

					if(robot.getState() == Robot.State.score){
						//Attempts to shoot
						checkShoot();
					}

					else{
						//If no move is available generates new path

						calculateMoves();
						collectionTarget.setTargeted(true);
					}
				}
				catch(NullPointerException exception){
					//Finds new paths
					calculateMoves();
					collectionTarget.setTargeted(true);
				}
				catch(IndexOutOfBoundsException e){
					//Finds new paths
					calculateMoves();
					collectionTarget.setTargeted(true);
				}
				//Waits for goAgain to set this to true on run this loop again
				running = false;
			}
		}
	}

	private void checkShoot() {
		//Checks to see if the robot is within range of the goal
		if(Field.distance(robot.getPosition(), RoboticsProjectPanel.field.findScoringZone(robot.getAlliance())) < 250){

			//shoots the ball
			robot.shoot();
			//Gets new robot state
			robot.findState();
			robotState = robot.getState();
		}
	}

	private void calculate(){

		//Calls recursive method to generate all paths
		findNewCollectPaths(moveList, robot.getPosition());


		//turns the pathList into an array that can be sorted
		pathListasArray = new Path[pathList.size()];
		for (int i = 0; i < pathList.size() ; i++){
			pathListasArray[i] = pathList.get(i);
		}

		//Sorts paths based on their distance from the target point
		PathSorting.selectionSort(pathListasArray);

		//puts the sorted list of Paths in pathList
		pathList.clear();
		Collections.addAll(pathList,(Path[]) pathListasArray);

	}

	private void pathFinding(Direction direction, ArrayList<Point> movesFound, Point currentPoint){

		switch (direction){
		case up: 
			//Defines where the path has gotten so far
			this.currentPoint = currentPoint;
			//creates the next point in the list based on the direction
			Point nextPoint = new Point((int) currentPoint.x, (int) (currentPoint.y-robot.getSpeed()));
			//creates and fills a new list with the old list
			moveList = new ArrayList<Point>();
			for (Point point : movesFound){
				moveList.add(point);
			}
			//adds the new point
			moveList.add(nextPoint);
			//If the path is long enough, ends the algorithm otherwise it gets called again
			if(moveList.size() >= movesKnown){
				end(moveList);				
			}
			else{				
				findNewCollectPaths(moveList, nextPoint);
			}
			break;
		case down: 
		{
			//Defines where the path has gotten so far
			this.currentPoint = currentPoint;
			//creates the next point in the list based on the direction
			Point nextPoint2 = new Point((int) currentPoint.x, (int) (currentPoint.y+robot.getSpeed()));
			//creates and fills a new list with the old list
			moveList = new ArrayList<Point>();
			for (Point point : movesFound){
				moveList.add(point);
			}
			//adds the new point
			moveList.add(nextPoint2);
			//If the path is long enough, ends the algorithm otherwise it gets called again
			if(moveList.size() >= movesKnown){
				end(moveList);				
			}
			else{	
				findNewCollectPaths(moveList, nextPoint2);
			}
		}
		break;

		case left:
		{
			//Defines where the path has gotten so far
			this.currentPoint = currentPoint;
			//creates the next point in the list based on the direction
			Point nextPoint3 = new Point((int) currentPoint.x - robot.getSpeed(), (int) (currentPoint.y));
			//creates and fills a new list with the old list
			moveList = new ArrayList<Point>();
			for (Point point : movesFound){
				moveList.add(point);
			}
			//adds the new point
			moveList.add(nextPoint3);
			//If the path is long enough, ends the algorithm otherwise it gets called again
			if(moveList.size() >= movesKnown){
				end(moveList);				
			}
			else{				
				findNewCollectPaths(moveList, nextPoint3);
			}
			break;
		}
		case right:{
			//Defines where the path has gotten so far
			this.currentPoint = currentPoint;
			Point nextPoint4 = new Point((int) currentPoint.x +robot.getSpeed(), (int) (currentPoint.y));
			//creates the next point in the list based on the direction
			moveList = new ArrayList<Point>();
			//creates and fills a new list with the old list
			for (Point point : movesFound){
				moveList.add(point);
			}
			//adds the new point
			moveList.add(nextPoint4);
			//If the path is long enough, ends the algorithm otherwise it gets called again
			if(moveList.size() >= movesKnown){
				end(moveList);				
			}
			else{
				findNewCollectPaths(moveList, nextPoint4);
			}
			break;
		}
		}
	}

	private void findNewCollectPaths(ArrayList<Point> newMoveList, Point newPoint) {
		//Checks to see if a move is valid and if it is creates a new path with that direction
		if(robot.moveIsValid(Direction.up)){
			pathFinding(Direction.up, newMoveList, newPoint);
		}
		if(robot.moveIsValid(Direction.down)){
			pathFinding(Direction.down, newMoveList, newPoint);
		}
		if(robot.moveIsValid(Direction.left)){
			pathFinding(Direction.left, newMoveList, newPoint);
		}
		if(robot.moveIsValid(Direction.right)){
			pathFinding(Direction.right, newMoveList, newPoint);
		}
	}
	
	private void end(ArrayList<Point> listOfMoves){
		switch (robotState){
		case collect:
			try{
				//find the collection target
				if(collectionTarget.isCarried()){
					collectionTarget = RoboticsProjectPanel.field.findClosestGamePiece(robot);

					if(!robot.moveIsValid(selectedPath.getPointAt(currentMove))){
						collectionTarget.setTargeted(false);
						collectionTarget = RoboticsProjectPanel.field.acquireNewTarget(robot, collectionTarget.getPosition());
					}
				}
			}
			catch(NullPointerException e){
				collectionTarget = RoboticsProjectPanel.field.findClosestGamePiece(robot);
			}

			//creates the new path and adds it to the path list with a destination of the gamepiece
			Path path = new Path(listOfMoves, collectionTarget.getPosition());

			pathList.add(path);
			break;
		case score:
			//creates a new path with the destination of the scoring zones
			Path path2 = new Path(listOfMoves, RoboticsProjectPanel.field.findScoringZone(robot.getAlliance()));
			pathList.add(path2);
			break;
		}
	}
	
	public enum Direction{
		up, down, left, right;
	}
	
	private void calculateMoves(){

		resetLists();
		//empties all the lists
		if (robotState == Robot.State.neutral){
			//gets the robot's state
			robot.findState();
			robotState = robot.getState();
		}

		switch (robotState) {
		case neutral:
			robot.findState();
			break;
		case collect:
			//runs the recursive algorithm
			calculate();

			break;
		case score:
			//runs the recursive algorithm
			calculate();
			break;


		case block:
			//not actually used yet......
			calculate();
			break;
		}
	}
	
	public void goAgain(){
		//makes the program begin to do stuff again
		running = true;
	}
	
	private void resetLists(){
		//empties the lists and resets the moves.
		currentMove = 0;
		pathList.clear();
		pathList = new ArrayList<Path>();
		moveList.clear();
		moveList = new ArrayList<Point>();
	}

	private void checkCollecting(){
		if(Field.distance(robot.getPosition(), collectionTarget.getPosition()) < 80){
			//Checks to see if the robot is in a position to collect 
			if(robot.pickup(collectionTarget) == true){

				// Gets the robots state
				robot.findState();
				robotState = robot.getState();

				ControlPanel.output("Robot state is " + robot.getState().toString());

				//clears lists and finds the new moves
				resetLists();
				calculateMoves();
			}
		}
	}
}
